package liuch;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author 17101651
 * @ClassName: ProductAndConsumeTest
 * @Description: 利用烤鸭的例子，模拟生产者和消费者模型
 * @data 2018年07月17日
 */
public class ProductAndConsumeTest {

    /**
     * KaoYaResource 普通的wait()及notifyAll()
     */
    static class KaoYaResource {
        private String name;// 生产者name
        private int count = 1; // 考鸭初试数量
        boolean flag = false;// 是否需要线程等待

        // 生产
        synchronized void product(String name) {
            if (flag) {
                // 如果有烤鸭
                try {
                    this.wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            this.name = name + count;
            count++;
            System.out.println(Thread.currentThread().getName() + "生产者..." + this.name);
            flag = true;// 有烤鸭返回
            notifyAll();
        }

        // 消费
        synchronized void consume(String name) {
            if (!flag) {
                // 如果没有烤鸭就等待
                try {
                    this.wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "消费者......." + this.name);
            flag = false;
            notifyAll();
        }
    }

    static class Producter implements Runnable {

        private KaoYaResource resource;

        Producter(KaoYaResource resource) {
            this.resource = resource;
        }

        @Override
        public void run() {
            try {
                while (true) {
                    Thread.sleep(1000);
                    resource.product("北京烤鸭");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    static class Consumer implements Runnable {
        private KaoYaResource resource;

        Consumer(KaoYaResource resource) {
            this.resource = resource;
        }

        @Override
        public void run() {
            try {
                while (true) {
                    Thread.sleep(1000);
                    resource.consume("北京烤鸭");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 通过对象锁的方式来实现等待/通知机制
     */
    static class KaoYaResourceByLock {
        private Lock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
        private int count = 1;
        private String name;
        boolean flag = false;

        // 生产者
        void product(String name) {
            lock.lock();
            try {
                while (flag) {
                    condition.await();
                }
                this.name = name + count;
                count++;
                System.out.println(Thread.currentThread().getName() + "生产者..." + this.name);
                flag = true;// 有烤鸭后flag = true
                condition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        // 消费
        void consume(String name) {
            lock.lock();
            try {
                while (!flag) {
                    condition.await();
                }
                System.out.println(Thread.currentThread().getName() + "消费者...." + this.name);
                flag = false;
                condition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

    }

    static class ProducterByLock implements Runnable {
        private KaoYaResourceByLock resourceByLock;

        ProducterByLock(KaoYaResourceByLock resourceByLock) {
            this.resourceByLock = resourceByLock;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(1000);
                    resourceByLock.product("北京烤鸭");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class ConsumerByLock implements Runnable {
        private KaoYaResourceByLock resourceByLock;

        ConsumerByLock(KaoYaResourceByLock resourceByLock) {
            this.resourceByLock = resourceByLock;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(100);
                    resourceByLock.consume("北京烤鸭");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 准备两组条件对象(Condition也称监视器)来实现等待/通知机制 一组监视生产者,一组监视消费者
     */
    static class ResourceBy2Condition {
        private String name;
        private int count = 1;
        boolean flag = false;

        // 创建一个锁对象
        Lock lock = new ReentrantLock();

        // 通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者
        Condition proCondition = lock.newCondition();
        Condition cumCondition = lock.newCondition();

        // 生产
        void product(String name) {
            lock.lock();
            try {
                while (flag) {
                    proCondition.await();
                }
                this.name = name + count;
                count++;
                System.out.println(Thread.currentThread().getName() + "生产者....." + this.name);
                flag = true;
                cumCondition.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        // 消费
        void consume(String name) {
            lock.lock();
            try {
                while (!flag) {
                    cumCondition.await();
                }
                System.out.println(Thread.currentThread().getName() + "消费者...." + this.name);
                flag = false;
                proCondition.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    static class ProducterBy2Condition implements Runnable {

        private ResourceBy2Condition resource;

        ProducterBy2Condition(ResourceBy2Condition resource) {
            this.resource = resource;
        }

        @Override
        public void run() {
            try {
                while (true) {
                    Thread.sleep(100);
                    resource.product("北京烤鸭");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    static class ConsumerBy2Condition implements Runnable {
        private ResourceBy2Condition resourceByLock;

        ConsumerBy2Condition(ResourceBy2Condition resourceByLock) {
            this.resourceByLock = resourceByLock;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(100);
                    resourceByLock.consume("北京烤鸭");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
//        KaoYaResource resource = new KaoYaResource();
//        Producter producter = new Producter(resource);
//        Consumer consumer = new Consumer(resource);
//        thread thread1 = new thread(producter);
//        thread thread2 = new thread(consumer);
//        thread thread3 = new thread(producter);
//        thread thread4 = new thread(consumer);
//        thread1.start();
//        thread3.start();
//        thread2.start();
//        thread4.start();

//        KaoYaResourceByLock resourceByLock = new KaoYaResourceByLock();
//        ProducterByLock producterByLock = new ProducterByLock(resourceByLock);
//        ConsumerByLock consumerByLock = new ConsumerByLock(resourceByLock);
//        thread thread1 = new thread(producterByLock);
//        thread thread11 = new thread(producterByLock);
//        thread thread2 = new thread(consumerByLock);
//        thread thread21 = new thread(consumerByLock);
//        thread1.start();
//        thread11.start();
//        thread2.start();
//        thread21.start();

        ResourceBy2Condition condition = new ResourceBy2Condition();
        ProducterBy2Condition producterBy2Condition = new ProducterBy2Condition(condition);
        ConsumerBy2Condition consumerBy2Condition = new ConsumerBy2Condition(condition);
        Thread thread1 = new Thread(producterBy2Condition);
        Thread thread2 = new Thread(producterBy2Condition);
        Thread thread3 = new Thread(consumerBy2Condition);
        Thread thread4 = new Thread(consumerBy2Condition);
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}
